package com.ywd.platform.netty.jt080.service;

import com.ywd.platform.netty.jt080.po.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * JT808协议编码器
 * 负责将JT808Message对象编码为符合JT808协议的字节数据
 * 功能包括：
 * 1. 消息头编码
 * 2. 消息体编码（支持多种消息类型）
 * 3. 校验码计算
 * 4. 转义处理
 * 5. 头尾标识添加
 */
@Slf4j
//@ChannelHandler.Sharable
public class JT808Encoder extends MessageToMessageEncoder<JT808Message> {

    @Override
    public void encode(ChannelHandlerContext ctx, JT808Message msg, List<Object> out) {

        log.info("[输出消息编码器] - 编码消息ID: 0x{}", msg.getHeader().getMsgId());

        ByteBuf contentBuf = null;
        ByteBuf escapedBuf = null;

        try {
            // 1. 编码消息内容（不含头尾标识和转义）
            contentBuf = encodeMessageContent(msg);

            // 2. 添加头尾标识并进行转义处理
            escapedBuf = wrapWithHeaderFlags(contentBuf);

            // 打印解析报文
            //log.info("输出消息报文内容: {}", ByteBufUtil.hexDump(escapedBuf));

            // 3. 添加到输出列表
            out.add(escapedBuf.retain());

            //log.info("输出消息编码完成,消息ID: 0x{}, 长度: {}字节",
            //Integer.toHexString(msg.getHeader().getMsgId()),
            //escapedBuf.readableBytes());

        } catch (Exception e) {
            log.error("输出消息编码失败,错误: {}", e.getMessage(), e);
            // 释放资源
            if (contentBuf != null) {
                contentBuf.release();
            }
            if (escapedBuf != null) {
                escapedBuf.release();
            }
        } finally {
            // 释放原始内容缓冲区
            if (contentBuf != null) {
                contentBuf.release();
            }
        }
    }


    /**
     * 编码消息内容（不含头尾标识和转义）
     */
    public ByteBuf encodeMessageContent(JT808Message msg) {
        //log.info("编码消息内容");
        ByteBuf buf = Unpooled.buffer();
        try {
            // 编码消息头
            encodeHeader(buf, msg.getHeader());
            // 编码消息体
            if (msg.getBody() != null) {
                encodeBody(buf, msg.getBody(), msg.getHeader().getMsgId());
            }
            // 计算并写入校验码
            byte checkCode = calculateCheckCode(buf);
            buf.writeByte(checkCode);
            return buf.retain();
        } finally {
            buf.release();
        }
    }

    /**
     * 编码消息头
     */
    public void encodeHeader(ByteBuf buf, JT808Header header) {
        //log.info("编码消息头");
        //[8100][属性][版本][手机号][流水号][应答流水号][结果][鉴权码][校验]
        try {
            // 1. 消息ID (2字节)
            int msgId = header.getMsgId();
            //log.info("消息ID: 0x{}", Integer.toHexString(msgId));
            buf.writeShort(msgId); // 直接写入short，不需要转换为十六进制字符串再解析

            // 2. 消息体属性 (2字节)
            int msgBodyProps = header.getMsgBodyProps();
            //log.info("消息体属性: 0x{}", Integer.toHexString(msgBodyProps));
            buf.writeShort(msgBodyProps); // 直接写入short

            // 3. 协议版本号 (1字节，2019版协议)
            // 只有当版本标识位为1时才写入协议版本号
            int version = header.getVersion();
            //log.info("协议版本号: 0x{}", version);
            buf.writeByte(version);

            // 4. 终端手机号 (10字节BCD码，JT808协议标准)
            String terminalPhone = header.getTerminalPhone();
            //log.info("终端手机号: {}", terminalPhone);
            encodeBCD10PhoneNumber(buf, terminalPhone);

            // 5. 消息流水号 (2字节)
            int flowId = header.getFlowId();
            //log.info("消息流水号: {}", flowId);
            buf.writeShort(flowId);

            // 6. 分包信息 (如果有分包)
            boolean hasSubPackage = header.isHasSubPackage();
            //log.info("是否有分包: {}", hasSubPackage);

            if (hasSubPackage) {
                int totalPackage = header.getTotalPackage();
                int packageNo = header.getPackageNo();
                //log.info("总包数: {}, 包序号: {}", totalPackage, packageNo);
                buf.writeShort(totalPackage);
                buf.writeShort(packageNo);
            }
        } catch (Exception e) {
            log.error("编码消息头失败: {}", e.getMessage(), e);
            //throw new RuntimeException("消息头编码失败", e);
        }
    }

    /**
     * 计算消息体长度
     */
    public int calculateBodyLength(byte result, String authCode) {
        // 固定部分: 应答流水号(2字节) + 结果(1字节)
        int length = 3;
        // 成功时添加鉴权码长度
        if (result == 0 && authCode != null && !authCode.isEmpty()) {
            try {
                length += authCode.getBytes("GBK").length;
            } catch (Exception e) {
                // 如果GBK编码失败，使用UTF-8编码
                length += authCode.getBytes(StandardCharsets.UTF_8).length;
            }
        }

        return length;
    }

    /**
     * 编码11位手机号为BCD(10)格式
     * 专门用于将11位手机号转换为20位数字并编码为10字节BCD码
     * 格式："15135916393" -> "00000000015135916393" -> 10字节BCD
     */
    public void encodeBCD10PhoneNumber(ByteBuf buf, String phoneNumber) {
        if (phoneNumber == null || phoneNumber.length() != 11) {
            // 写入10字节的0作为默认值
            buf.writeZero(10);
            log.error("手机号必须为11位数字，使用默认值填充");
            return;
        }
        try {
            // 去除非数字字符
            String digitsOnly = phoneNumber.replaceAll("[^0-9]", "");
            // 验证手机号长度
            if (digitsOnly.length() != 11) {
                log.error("手机号必须为11位数字: {}", phoneNumber);
                // 写入10字节的0作为默认值
                buf.writeZero(10);
                return;
            }
            // 前面补9个0成为20位数字
            String paddedNumber = "000000000" + digitsOnly;
            //log.info("编码手机号BCD(10): {}", paddedNumber);
            // 将每两位数字编码为一个BCD字节
            for (int i = 0; i < 10; i++) {
                int offset = i * 2;
                char digit1Char = paddedNumber.charAt(offset);
                char digit2Char = paddedNumber.charAt(offset + 1);

                int digit1 = Character.digit(digit1Char, 10);
                int digit2 = Character.digit(digit2Char, 10);

                byte bcdByte = (byte) ((digit1 << 4) | digit2);
                buf.writeByte(bcdByte);
            }
        } catch (Exception e) {
            log.error("编码手机号失败: {}, 使用默认值", phoneNumber, e);
            // 写入10字节的0作为默认值
            buf.writeZero(10);
        }
    }


    /**
     * 编码消息体
     */
    public void encodeBody(ByteBuf buf, Object body, int msgId) {
        try {
            switch (msgId) {
                case JT808Protocol.MSG_ID_SERVER_REGISTER_RESP:   // 注册应答 0x8100
                    encodeRegisterResponse(buf, (RegisterResponse) body);
                    break;
                case JT808Protocol.MSG_ID_SERVER_COMMON_RESP:     // 通用应答 0x8001
                    encodeCommonResponse(buf, (CommonResponse) body);
                    break;
//                case JT808Protocol.MSG_ID_SERVER_SET_PARAM:     // 设置终端参数 0x8103
//                    log.info("设置终端参数消息体");
//                    encodeSetTerminalParams(buf, (TerminalParamBody) body);
//                    break;
//                case JT808Protocol.MSG_ID_SERVER_SELECT_PARAM: // 查询终端参数 0x8104
//                    log.info("查询终端参数消息体");
//                    encodeQueryTerminalParams(buf, (QueryParamBody) body);
//                    break;
                default:
                    encodeUnknownBody(buf, body, msgId);
            }
        } catch (Exception e) {
            log.error("编码消息体失败, 消息ID: 0x{}, 错误: {}",
                    Integer.toHexString(msgId), e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 编码注册应答(0x8100)消息体
     */
    public void encodeRegisterResponse(ByteBuf buf, RegisterResponse body) {

        //log.info("应答流水号, {}", body.getResponseFlowId());
        buf.writeShort(body.getResponseFlowId()); // 应答流水号
        //log.info("结果, {}", body.getResult());
        buf.writeByte(body.getResult());          // 结果

        // 只有成功时才包含鉴权码
        if (body.getResult() == 0x00 && body.getAuthCode() != null) {
            try {
                byte[] authBytes = body.getAuthCode().getBytes(JT808Protocol.DEFAULT_CHARSET);
                buf.writeBytes(authBytes);
            } catch (Exception e) {
                byte[] authBytes = body.getAuthCode().getBytes(StandardCharsets.UTF_8);
                buf.writeBytes(authBytes);
            }
        }
    }


    /**
     * 编码通用应答(0x8001)消息体
     */
    public void encodeCommonResponse(ByteBuf buf, CommonResponse body) {
        //log.info("应答流水号, {}", body.getResponseFlowId());
        buf.writeShort(body.getResponseFlowId()); // 应答流水号

        //log.info("应答消息ID, {}", body.getResponseMsgId());
        buf.writeShort(body.getResponseMsgId());  // 应答消息ID

        //log.info("结果, {}", body.getResult());
        buf.writeByte(body.getResult());          // 结果
    }

    /**
     * 编码未知消息体
     */
    public void encodeUnknownBody(ByteBuf buf, Object body, int msgId) {
        //log.warn("未知消息类型编码: 0x{}", Integer.toHexString(msgId));

        if (body instanceof byte[]) {
            // 直接写入字节数组
            buf.writeBytes((byte[]) body);
        } else {
            // 尝试转换为字符串编码
            String bodyStr = body.toString();
            byte[] bytes = bodyStr.getBytes(StandardCharsets.ISO_8859_1);
            buf.writeBytes(bytes);
        }
    }

    /**
     * 计算校验码（异或校验）
     */
    public byte calculateCheckCode(ByteBuf buf) {
        byte checkCode = 0;
        int length = buf.readableBytes();

        // 保存当前读取位置
        int readerIndex = buf.readerIndex();

        for (int i = 0; i < length; i++) {
            checkCode ^= buf.getByte(readerIndex + i);
        }

        return checkCode;
    }

    /**
     * 添加头尾标识并进行转义处理
     */
    public ByteBuf wrapWithHeaderFlags(ByteBuf content) {
        ByteBuf buf = Unpooled.buffer();

        try {
            // 写入起始标识
            buf.writeByte(JT808Protocol.HEADER_FLAG);

            // 写入转义后的内容
            int length = content.readableBytes();
            for (int i = 0; i < length; i++) {
                byte b = content.getByte(content.readerIndex() + i);

                if (b == JT808Protocol.HEADER_FLAG) {
                    // 0x7E -> 0x7D 0x02
                    buf.writeByte(JT808Protocol.ESCAPE_FLAG);
                    buf.writeByte(0x02);
                } else if (b == JT808Protocol.ESCAPE_FLAG) {
                    // 0x7D -> 0x7D 0x01
                    buf.writeByte(JT808Protocol.ESCAPE_FLAG);
                    buf.writeByte(0x01);
                } else {
                    buf.writeByte(b);
                }
            }
            // 写入结束标识
            buf.writeByte(JT808Protocol.HEADER_FLAG);
            return buf.retain();
        } finally {
            buf.release();
        }
    }





//
//    // ======================== 其他消息体编码方法 ============================================
//    //=====================================================================================
//
//
//    public void encodeQueryTerminalParams(ByteBuf buf, QueryParamBody body) {
//        buf.writeByte(body.getParamCount());
//        for (Integer paramId : body.getParamIds()) {
//            buf.writeInt(paramId);
//        }
//    }
//
//    public void encodeLocationQuery(ByteBuf buf, LocationQueryBody body) {
//        // 实现位置信息查询编码
//    }
//
//    public void encodeTextMessage(ByteBuf buf, TextMessageBody body) {
//        buf.writeByte(body.getFlag()); // 标志
//
//        try {
//            byte[] textBytes = body.getText().getBytes(JT808Protocol.DEFAULT_CHARSET);
//            buf.writeByte(textBytes.length); // 文本长度
//            buf.writeBytes(textBytes);      // 文本内容
//        } catch (UnsupportedEncodingException e) {
//            log.error("编码文本信息失败, 使用默认编码", e);
//            byte[] textBytes = body.getText().getBytes(StandardCharsets.UTF_8);
//            buf.writeByte(textBytes.length);
//            buf.writeBytes(textBytes);
//        }
//    }
//
//    public void encodePhoneCallBack(ByteBuf buf, PhoneCallBody body) {
//        buf.writeByte(body.getFlag()); // 标志
//
//        byte[] phoneBytes = body.getPhoneNumber().getBytes(StandardCharsets.US_ASCII);
//        buf.writeByte(phoneBytes.length); // 电话号码长度
//        buf.writeBytes(phoneBytes);       // 电话号码
//    }
//
//    public void encodeVehicleControl(ByteBuf buf, VehicleControlBody body) {
//        buf.writeByte(body.getCommand()); // 控制命令
//    }
//
//    public void encodeUnknownBody(ByteBuf buf, Object body, int msgId) {
//        log.warn("未知消息类型编码: 0x{}", Integer.toHexString(msgId));
//
//        if (body instanceof byte[]) {
//            // 直接写入字节数组
//            buf.writeBytes((byte[]) body);
//        } else {
//            // 尝试转换为字符串编码
//            String bodyStr = body.toString();
//            byte[] bytes = bodyStr.getBytes(StandardCharsets.ISO_8859_1);
//            buf.writeBytes(bytes);
//        }
//    }
}